﻿#include "getopt.h"

#include "com_log.h"
#include "test_bb_tun.h"
#include <getopt.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <thread>

// print usage
int usage(void)
{
    printf("This is a test program to demo baseband transport function!\n");
    printf("Option List:\n");
    printf(" -h --help              print usage which you're seeing\n");
    printf(" -u --user              specify the baseband user id\n");
    printf(" -p --port              specify the transport id\n");
    printf(" -i --ip                specify the tun device ip\n");
    printf(" -d --dev               specify the tun device name\n");
    printf(" -v --debug             debug mode in ethernet transfer\n");
    printf(" -r --rx_buf            buffer len of rx\n");
    printf(" -t --tx_buf            buffer len of tx\n");

    return 0;
}

static void tun_2_bb_thread(bb_tun_cfg& cfg)
{
    // allocate buffer
    unsigned char* pkg_buf = (unsigned char*)malloc(cfg.buff_max);

    if (!pkg_buf) {
        printf("tun2bb alloc memory error exit !!\n");
        return;
    }

    while (1) {
        int rdlen = cfg.dev.read(pkg_buf, cfg.buff_max);

        if (rdlen <= 0) {
            continue;
        }

        if (cfg.debugflg) {
            com_log(COM_SOCKET_DATA, "tun read = %d", rdlen);
        }

        int wrlen = bb_socket_write(cfg.bb_fd, pkg_buf, rdlen, -1);
        if (cfg.debugflg) {
            com_log(COM_SOCKET_DATA, "bb write = %d", wrlen);
        }
    }
}

static void bb_2_tun_thread(bb_tun_cfg& cfg)
{
    // allocate buffer
    char* pkg_buf = (char*)malloc(cfg.buff_max);
    if (!pkg_buf) {
        printf("bb2tun alloc memory error exit !!\n");
        return;
    }

    int offset = 0;
    while (1) {
        int len = bb_socket_read(cfg.bb_fd, pkg_buf, cfg.buff_max, -1);
        if (len < 0) {
            // 基带断开链接，暂不支持重连
            continue;
        }

        if (cfg.debugflg) {
            com_log(COM_SOCKET_DATA, "bb read raw= %d", len);
        }

        int wrlen = cfg.dev.write(pkg_buf, len);
        if (cfg.debugflg) {
            com_log(COM_SOCKET_DATA, "tun write = %d", wrlen);
        }
    }
}

static int tun_test(bb_tun_cfg& cfg)
{
    int ret = bb_host_connect(&cfg.phost, "127.0.0.1", BB_PORT_DEFAULT);
    if (ret) {
        printf("connect failed = %d\n", ret);
        return ret;
    }
    bb_sock_opt_t opt;

    bb_dev_t** devs;

    int sz = bb_dev_getlist(cfg.phost, &devs);
    if (sz <= 0) {
        printf("dev cnt = 0\n");
        return sz;
    }

    cfg.pdev = bb_dev_open(devs[0]);
    if (!cfg.pdev) {
        printf("can't open dev!!!\n");
        return -1;
    }

#if 0
    if (cfg.ipset_flg) {
        cfg.tun_fd = tun_alloc(cfg.devname, cfg.ip, "255.255.255.0", cfg.mtu);
        printf("dev = %s,ip = %s,tun_fd = %d\n", cfg.devname, cfg.ip, cfg.tun_fd);
    } else {
        cfg.tun_fd = tun_alloc(cfg.devname, nullptr, nullptr, cfg.mtu);
        printf("dev = %s,tun_fd = %d\n", cfg.devname, cfg.tun_fd);
    }
#else
    cfg.dev.name(cfg.devname);
    if (cfg.ipset_flg) {
        cfg.dev.ip(cfg.ip, 24);
        cfg.dev.mtu(cfg.mtu);
        printf("dev = %s,ip = %s,mtu = %d , tun_fd = %" PRIi64 "\n",
               cfg.devname,
               cfg.ip,
               cfg.dev.mtu(),
               (uint64_t)cfg.dev.native_handle());
    }
    cfg.dev.up();
#endif

    // open bb port
    opt.rx_buf_size = cfg.rx_buf_len;
    opt.tx_buf_size = cfg.tx_buf_len;

    cfg.bb_fd = bb_socket_open(cfg.pdev,
                               cfg.slot_id,
                               cfg.port_id,
                               BB_SOCK_FLAG_RX | BB_SOCK_FLAG_TX | BB_SOCK_FLAG_DATAGRAM,
                               &opt);
    if (cfg.bb_fd < 0) {
        printf("create bb socket failed!\n");
    } else {
        printf("ar_bb_socket_fd = %d\n", cfg.bb_fd);
    }

    std::thread tun_bb(tun_2_bb_thread, std::ref(cfg));
    std::thread bb_tun(bb_2_tun_thread, std::ref(cfg));

    tun_bb.join();
    bb_tun.join();

    return 0;
}

int main(int argc, char* argv[])
{
    int         opt           = 0;
    int         flag_help     = 0;
    const char* short_options = "hp:i:u:d:vr:t:";
    uint32_t    rx_buffer     = 40000;
    uint32_t    tx_buffer     = 60000;


    bb_tun_cfg    cfg;
    struct option long_options[] = {
        {"help",   no_argument,       NULL, 'h'},
        { "port",  required_argument, NULL, 'p'},
        { "ip",    required_argument, NULL, 'i'},
        { "user",  required_argument, NULL, 'u'},
        { "dev",   required_argument, NULL, 'e'},
        { "debug", no_argument,       NULL, 'v'},
        { "rx_buf",required_argument, NULL, 'r'},
        { "tx_buf",required_argument, NULL, 't'},
        { 0,       0,                 0,    0  },
    };

    while ((opt = getopt_long(argc, argv, short_options, long_options, NULL)) != -1) {
        switch (opt) {
        case 'h':
            flag_help = 1;
            break;
        case 'p':
            cfg.port_id = strtoul(optarg, NULL, 10);
            break;
        case 'i':
            strcpy(cfg.ip, optarg);
            cfg.ipset_flg = 1;
            break;
        case 'u':
            cfg.slot_id = (bb_slot_e)strtoul(optarg, NULL, 10);
            break;
        case 'd':
            strcpy(cfg.devname, optarg);
            break;
        case 'v':
            cfg.debugflg = 1;
            break;
        case 'r':
            cfg.rx_buf_len = strtoul(optarg, NULL, 10);
            break;
        case 't':
            cfg.tx_buf_len = strtoul(optarg, NULL, 10);
            break;
        default:
            printf("unknown option\n");
            break;
        }
    }

    if (optind < argc) {
        printf("non-option ARGV-elements: ");
        while (optind < argc)
            printf("%s ", argv[optind++]);
        printf("\n");
    }

    if (flag_help) {
        return usage();
    }

    return tun_test(cfg);
}
